package security.keyManagement;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStore.PasswordProtection;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;

import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;

public class KeyStoreManager {

	private KeyStore _keyStore;

	private String _keyStoreAddress;

	private PasswordProtection _passwordProtection;

	private static final String _originalCertcopKeyStoreAddress = "config/CERTCOP.keystore";

	private static final String _originalVericopKeyStoreAddress = "config/VERICOP.keystore";

	private static final String _originalClientKeyStoreAddress = "config/Client.keystore";

	public KeyStoreManager(boolean server, boolean certcop){
		if(server){
			if(certcop)
				_keyStoreAddress = _originalCertcopKeyStoreAddress;
			else
				_keyStoreAddress = _originalVericopKeyStoreAddress;
		}			
		else
			_keyStoreAddress = _originalClientKeyStoreAddress;
	}

	public KeyStoreManager(String password, boolean server, boolean certcop) throws IOException{
		if(server){
			if(certcop)
				_keyStoreAddress = _originalCertcopKeyStoreAddress;
			else
				_keyStoreAddress = _originalVericopKeyStoreAddress;
		}else
			_keyStoreAddress = _originalClientKeyStoreAddress;

		try {
			File file = new File(_keyStoreAddress);
			file.setReadOnly();
			FileInputStream fis = new FileInputStream(file);

			_keyStore = KeyStore.getInstance("JCEKS");

			if(password != null){
				_keyStore.load(fis, password.toCharArray());
				_passwordProtection = new PasswordProtection(password.toCharArray());
			}
			else
				_keyStore.load(fis, "".toCharArray());			

		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (CertificateException e) {
			e.printStackTrace();
		} catch (KeyStoreException e) {
			e.printStackTrace();
		} 
	}

	public String getPassword(){
		return String.valueOf(_passwordProtection.getPassword());
	}

	public PrivateKey getServerPrivateKey(boolean certcop) {
		if(certcop){
			try {
				return (PrivateKey) _keyStore.getKey("CERTCOPServerKeyPair", _passwordProtection.getPassword());
			} catch (UnrecoverableKeyException e) {
				e.printStackTrace();
			} catch (KeyStoreException e) {
				e.printStackTrace();
			} catch (NoSuchAlgorithmException e) {
				e.printStackTrace();
			}
		}else{
			try {
				return (PrivateKey) _keyStore.getKey("VERICOPServerKeyPair", _passwordProtection.getPassword());
			} catch (UnrecoverableKeyException e) {
				e.printStackTrace();
			} catch (KeyStoreException e) {
				e.printStackTrace();
			} catch (NoSuchAlgorithmException e) {
				e.printStackTrace();
			}
		}
		return null;
	}
	
	public PublicKey getServerPublicKey(boolean certcop){
		try {
			if(certcop){
			Certificate certcopCert = _keyStore.getCertificateChain("CERTCOPServerKeyPair")[0];
			return certcopCert.getPublicKey();
			}else{
				Certificate vericopCert = _keyStore.getCertificateChain("VERICOPServerKeyPair")[0];
				return vericopCert.getPublicKey();
			}
		} catch (KeyStoreException e) {
			e.printStackTrace();
		}
		return null;
	}

	public KeyPair getVericopKeyPair(){
		try {
			PrivateKey privateKey =  (PrivateKey) _keyStore.getKey("VERICOPServerKeyPair", _passwordProtection.getPassword());
			Certificate vericopCert = _keyStore.getCertificateChain("VERICOPServerKeyPair")[0];

			KeyPair keyPair = new KeyPair(vericopCert.getPublicKey(), privateKey);
			return keyPair;
		} catch (UnrecoverableKeyException e) {
			e.printStackTrace();
		} catch (KeyStoreException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}

		return null;
	}

	public PublicKey getVericopPublicKey() {
		try {
			Certificate vericopCert = _keyStore.getCertificate("VERICOPServerCert");			
			return vericopCert.getPublicKey();
		} catch (KeyStoreException e) {
			e.printStackTrace();
		}

		return null;
	}

	public PublicKey getCertcopPublicKey(){
		try {
			Certificate certcopCert = _keyStore.getCertificate("CERTCOPServerCert");
			return certcopCert.getPublicKey();
		} catch (KeyStoreException e) {
			e.printStackTrace();
			return null;
		}
	}

	public PublicKey getWebServerPublicKey() {
		try {
			Certificate webServerCert = _keyStore.getCertificate("WebServerCert");
			return webServerCert.getPublicKey();
		} catch (KeyStoreException e) {
			e.printStackTrace();
			return null;
		}
	}

	public boolean validatePassword(char[] password) {
		boolean result = true;
		char[] systemPassword = _passwordProtection.getPassword();

		if(systemPassword.length != password.length)
			result =  false;	
		else
			for(int index = 0; index < systemPassword.length; index++){
				if(systemPassword[index]!=password[index])
					result = false;
			}
		
		Arrays.fill(password, (char)0);
		return result;
	}

	public String getDBServerPassword(){
		try{
			SecretKey key  = (SecretKey) _keyStore.getKey("MySQLPassword", _passwordProtection.getPassword());

			SecretKeyFactory skf = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
			PBEKeySpec keySpec = (PBEKeySpec) skf.getKeySpec(key, PBEKeySpec.class);

			return new String(keySpec.getPassword());
		}catch(KeyStoreException e){
			e.printStackTrace();
			return null;
		} catch (UnrecoverableKeyException e) {
			e.printStackTrace();
			return null;
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return null;
		} catch (InvalidKeySpecException e) {
			e.printStackTrace();
			return null;
		}
	}

	public String getCertcopCertSerial() {
		try {
			X509Certificate certcopCert = (X509Certificate) _keyStore.getCertificateChain("CERTCOPServerKeyPair")[0];
			return certcopCert.getSerialNumber().toString();
		} catch (KeyStoreException e) {
			e.printStackTrace();
			return null;
		}
	}

	protected void finalize() throws Throwable{
		try{
			FileOutputStream fos = new FileOutputStream(_keyStoreAddress);

			_keyStore.store(fos,_passwordProtection.getPassword());

			fos.flush();
			fos.close();

			_passwordProtection.destroy();			
		}finally{
			super.finalize();
		}
	}
}
